/*	This file is part of AstroAttack.
*
*	AstroAttack is free software; you can redistribute it and/or modify
*	it under the terms of the GNU General Public License as published by
*	the Free Software Foundation; either version 2 of the License, or
*	(at your option) any later version.
*	
*	AstroAttack is distributed in the hope that it will be useful,
*	but WITHOUT ANY WARRANTY; without even the implied warranty of
*	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*	GNU General Public License for more details.
*	
*	You should have received a copy of the GNU General Public License
*	along with AstroAttack; if not, write to the Free Software
*	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*	
*	$Id: WorldManager.java 76 2007-03-07 03:40:51Z cornfused36 $
*/

package AstroAttack;

import java.awt.Image;
import java.awt.geom.Point2D;

import java.util.Vector;
import java.util.ArrayList;

import java.io.File;

/** Manages the loading of world and level files, keeps track of levels, etc. */
public class WorldManager
{
	private static WorldManager m_ref;
	
	public static WorldManager getManager()
	{
		if ( m_ref == null )
			m_ref = new WorldManager();
		return m_ref;
	}
	
	private String m_worldsDir;
	private INIParser m_worldFile;
	
	private String m_worldName;
	private int m_levelCount;
	private String m_shipBulletIdent;
	private String m_shipImageIdent;
	
	private int curLevel;
	private Level [] levels;
	
	private WorldManager()
	{
		m_worldsDir = "data/worlds/";
		curLevel=0;
	}
	
	public void loadWorld(String world)
	{
		// check if the world exists
		// if ( !worldExists(world) )
			//throw new Exception("World not found: " + world);
		
		File worldFile = new File(m_worldsDir + world + "/" + world + ".world");
		
		// load it into an INI parser
		m_worldFile = new INIParser(worldFile);
		m_worldFile.load();
		
		// make sure the world file is valid
		try { checkWorldFile(m_worldFile); }
		catch ( Exception e )
		{
			System.err.println("Error loading world file: " + e.getMessage());
		}
		
		m_worldName = m_worldFile.get("general", "name");
		
		try { m_levelCount = Integer.parseInt(m_worldFile.get("general", "levels")); }
		catch ( Exception e ) { System.err.println(e); }
		
		m_shipBulletIdent = m_worldFile.get("ship", "bullet");
		m_shipImageIdent = m_worldFile.get("ship", "image");
		
		// set the image cache path to the world path
		String imgPath = "data/worlds/" + world + "/" + m_worldFile.get("general", "gfxdir");
		ImageCache.getCache().setImgPath(imgPath);
		
		// load all levels
		loadLevels(world);
		// load all images
		loadImages(m_worldFile);
	}
	
	private void loadLevels(String world)
	{
		levels = new Level[m_levelCount];
		for(int i=0;i<m_levelCount;i++)
		{
			levels[i] = new Level();
			File levelFile = new File(m_worldsDir + world + "/" + i + ".level");
			INIParser lvlFile = new INIParser(levelFile);
			lvlFile.load();
			int numWaves = Integer.parseInt(lvlFile.get("formation","waves"));
			for(int j=1;j<=numWaves;j++)
			{
				int [] na = new int[3];
				for(int n=0;n<3;n++)
					na[n] = Integer.parseInt(lvlFile.get("formation","wave"+j+".row"+(n+1)+".count"));
				String [] at = new String[3];
				for(int n=0;n<3;n++)
					at[n] = lvlFile.get("formation","wave"+j+".row"+(n+1)+".enemy");
				levels[i].addWave(new Wave(na,at));
			}
		}
	}
	
	public String getShipImage()
	{
		return m_shipImageIdent;
	}
	
	public String getShipBullet()
	{
		return m_shipBulletIdent;
	}
	
	public void loadImages(INIParser worldFile)
	{
		Vector<String> imgs = new Vector<String>();
		
		String shipImg = worldFile.get("ship", "image");
		imgs.add(shipImg);
		
		//String backgroundImg = worldFile.get("general", "background");
		//imgs.add(backgroundImg);
		
		int bulletCount = Integer.parseInt(worldFile.get("bullets", "count"));
		for ( int i = 0; i < bulletCount; ++i )
		{
			String bulletImg = worldFile.get("bullets", "bullet" + i + ".image");
			imgs.add(bulletImg);
		}
		
		int enemyCount = Integer.parseInt(worldFile.get("enemies", "count"));
		for ( int i = 0; i < enemyCount; ++i )
		{
			String enemyImg = worldFile.get("enemies", "enemy" + i + ".image");
			imgs.add(enemyImg);
		}
		
		Debug.println(imgs);
		
		ImageCache.getCache().loadAll(imgs);
	}
	
	/*public Image getBackground()
	{
		ImageCache ic = ImageCache.getCache();
		String imgName = m_worldFile.get("general", "background");
		return ic.getImage(imgName);
	}*/
	
	public Bullet createBullet(String ident, Point2D point, int team, int direction)
	{
		// ident is either bullet0, bullet1, ..., bulletx
		
		ImageCache ic = ImageCache.getCache();
		String imgName = m_worldFile.get("bullets", ident + ".image");
		Image img = ic.getImage(imgName);
		
		int damage = Integer.parseInt(m_worldFile.get("bullets", ident + ".damage"));
		int speed = Integer.parseInt(m_worldFile.get("bullets", ident + ".speed"));
		point.setLocation(point.getX()+(-1*img.getWidth(null)/2),point.getY());
		
		return new Bullet(img, point, speed, team, damage, direction);
	}
	
	public Alien createAlien(String ident, Point2D point, int direction, int enter)
	{
		ImageCache ic = ImageCache.getCache();
		String imgName = m_worldFile.get("enemies", ident + ".image");
		Image img = ic.getImage(imgName);
		
		int speed = Integer.parseInt(m_worldFile.get("enemies", ident + ".speed"));
		
		int m = Integer.parseInt(m_worldFile.get("enemies", ident + ".movement"));
		Alien.Movement movement;
		switch ( m )
		{
			case 0:
				movement = Alien.Movement.BLOCK;
				break;
			case 1:
				movement = Alien.Movement.DIAGONAL;
				break;
			case 2:
				movement = Alien.Movement.RANDOM;
				break;
			case 3:
				movement = Alien.Movement.CIRCLES;
				break;
			default:
				movement = null;
				break;
		}
		
		int s = Integer.parseInt(m_worldFile.get("enemies", ident + ".shoot"));
		Alien.Shoot shoot;
		switch ( s )
		{
			case 0:
				shoot = Alien.Shoot.STRAIGHT;
				break;
			case 1:
				shoot = Alien.Shoot.ANGLED;
				break;
			case 2:
				shoot = Alien.Shoot.MULTIPLE;
				break;
			default:
				shoot = null;
				break;
		}
		
		int health = Integer.parseInt(m_worldFile.get("enemies", ident + ".health"));
		boolean attacks = !(s == -1);
		
		return new Alien(img, point, direction, speed, movement, shoot, health, enter, attacks);
	}
	
	public boolean worldExists(String world)
	{
		File worldDir = new File(m_worldsDir + world);
		File worldFile = new File(m_worldsDir + world + "/" + world + ".world");
		
		return worldDir.exists() && worldFile.exists();
	}
	
	public void checkWorldFile(INIParser worldFile) throws Exception
	{
		// this method should probably be fixed up.. like telling what the
		// error is instead of just returning false
		
		if ( worldFile.get("general", "levels") == null )
			throw new Exception("general.levels not set");
		
		int levelCount = 0;
		try { levelCount = Integer.parseInt(worldFile.get("general", "levels")); }
		catch ( Exception e ) { throw new Exception("general.levels not an integer"); }
		
		for ( int i = 0; i < levelCount; ++i )
		{
			File level = new File(worldFile.getFile().getParent() + "/level." + i);
			if ( !level.exists() )
				throw new Exception("missing level file: level." + i);
		}
		
		if ( worldFile.get("general", "gfxdir") == null )
			throw new Exception("general.gfxdir not set");
		
		if ( worldFile.get("ship", "image") == null )
			throw new Exception("ship.image not set");
		if ( worldFile.get("ship", "bullet") == null )
			throw new Exception("ship.bullet not set");
		
		if ( worldFile.get("bullets", "count") == null )
			throw new Exception("bullets.count not set");
		
		// now check that all bullets are defined!
		int bulletCount = 0;
		try { bulletCount = Integer.parseInt(worldFile.get("bullets", "count")); }
		catch ( Exception e ) { throw new Exception("bullets.count not an integer"); }
		
		for ( int i = 0; i < bulletCount; ++i )
		{
			if ( worldFile.get("bullets", "bullet" + i + ".image") == null )
				throw new Exception("bullets.bullet" + i + ".image not set");
			if ( worldFile.get("bullets", "bullet" + i + ".damage") == null )
				throw new Exception("bullets.bullet" + i + ".damage not set");
			if ( worldFile.get("bullets", "bullet" + i + ".speed") == null )
				throw new Exception("bullets.bullet" + i + ".speed not set");
		}
		
		if ( worldFile.get("enemies", "count") == null )
			throw new Exception("enemies.count not set");
		
		int enemyCount = 0;
		try { enemyCount = Integer.parseInt(worldFile.get("enemies", "count")); }
		catch ( Exception e ) { throw new Exception("enemies.count not an integer"); }
		
		for ( int i = 0; i < enemyCount; ++i )
		{
			if ( worldFile.get("enemies", "enemy" + i + ".image") == null )
				throw new Exception("enemies.enemy" + i + ".image not set");
			if ( worldFile.get("enemies", "enemy" + i + ".bullet") == null )
				throw new Exception("enemies.enemy" + i + ".bullet not set");
			if ( worldFile.get("enemies", "enemy" + i + ".speed") == null )
				throw new Exception("enemies.enemy" + i + ".speed not set");
		}
	}
	
	public Wave next()
	{
		return levels[0].next();
	}
	public int nextLevel()
	{
		++curLevel;
		if(curLevel==m_levelCount)
			return -1;
		return curLevel;
	}
	
	public class Level
	{
		private ArrayList<Wave> waves;
		
		public Level()
		{
			waves = new ArrayList<Wave>();
		}
		
		public void addWave(Wave w)
		{
			waves.add(w);
		}
		
		public Wave next()
		{
			if(waves.size()!=0)
				return waves.remove(0);
			return null;
		}
	}
}
